﻿using System;

namespace Raven
{
    public abstract class MovingEntity : BaseGameEntity
    {
        protected Vector2 _velocity;
        protected Vector2 _heading;
        protected Vector2 _side;
        protected Fix _mass;
        protected Fix _maxSpeed;
        protected Fix _maxForce;
        protected Fix _maxTurnRate;

        public MovingEntity(EntityType type, Vector2 position, Fix radius, Vector2 velocity, Fix maxSpeed,
            Vector2 heading,
            Fix mass, Fix turnRate, Fix maxForce) : base(type)
        {
            _heading = heading;
            _velocity = velocity;
            _mass = mass;
            _side = _heading.Perp();
            _maxSpeed = maxSpeed;
            _maxTurnRate = turnRate;
            _maxForce = maxForce;
            _position = position;
            m_dBoundingRadius = radius;
        }

        //accessors
        public Vector2 Velocity()
        {
            return _velocity;
        }

        public void SetVelocity(Vector2 NewVel)
        {
            _velocity = NewVel;
        }

        public Fix Mass()
        {
            return _mass;
        }

        public Vector2 Side()
        {
            return _side;
        }

        public Fix MaxSpeed()
        {
            return _maxSpeed;
        }

        public void SetMaxSpeed(Fix new_speed)
        {
            _maxSpeed = new_speed;
        }

        public Fix MaxForce()
        {
            return _maxForce;
        }

        public void SetMaxForce(Fix mf)
        {
            _maxForce = mf;
        }

        public bool IsSpeedMaxedOut()
        {
            return _maxSpeed * _maxSpeed >= _velocity.sqrMagnitude;
        }

        public Fix Speed()
        {
            return _velocity.magnitude;
        }

        public Fix SpeedSq()
        {
            return _velocity.sqrMagnitude;
        }

        public Vector2 Heading()
        {
            return _heading;
        }

        public void SetHeading(Vector2 new_heading)
        {
            _heading = new_heading;

            //the side vector must always be perpendicular to the heading
            _side = _heading.Perp();
        }

        public bool RotateHeadingToFacePosition(Vector2 target)
        {
            Vector2 toTarget = target - _position.normalized;

            Fix dot = Vector2.Dot(_heading, toTarget);

            //some compilers lose acurracy so the value is clamped to ensure it
            //remains valid for the acos
            dot = FixMath.Clamp(dot, -1, 1);

            //first determine the angle between the heading vector and the target
            Fix angle = FixMath.Acos(dot);

            //return true if the player is facing the target
            if (angle < Fix.Epsilon) return true;

            //clamp the amount to turn to the max turn rate
            if (angle > _maxTurnRate) angle = _maxTurnRate;

            //The next few lines use a rotation matrix to rotate the player's heading
            //vector accordingly
            C2DMatrix RotationMatrix = new C2DMatrix();

            //notice how the direction of rotation has to be determined when creating
            //the rotation matrix
            RotationMatrix.Rotate(angle * _heading.Sign(toTarget));
            RotationMatrix.TransformVector2s(ref _heading);
            RotationMatrix.TransformVector2s(ref _velocity);

            //finally recreate m_vSide
            _side = _heading.Perp();

            return false;
        }

        public Fix MaxTurnRate()
        {
            return _maxTurnRate;
        }

        public void SetMaxTurnRate(Fix val)
        {
            _maxTurnRate = val;
        }
    }
}